const { Op } = require("sequelize");
const { Area, AreaIcon, Icon } = require("../../../../models");
const utils = require("../../../../utils");
module.exports = {
  // 创建/编辑区域
  async saveOrUpdate(req, res, next) {
    try {
      const { id } = req.body;
      return id
        ? this.updateArea(req, res, next)
        : this.saveArea(req, res, next);
    } catch (error) {
      next(error);
    }
  },

  // 创建区域
  async saveArea(req, res, next) {
    try {
      const user = await Area.create(req.body);
      return user ? res.cc(null, "成功创建区域") : res.err("创建区域失败");
    } catch (error) {
      next(error);
    }
  },

  // 更新区域
  async updateArea(req, res, next) {
    try {
      // 1、校验区域是否存在
      let areaItem = await this.checkAreaInfo(req, res, next);
      if (!areaItem) return;
      // 2、组装参数
      await utils.assignEditValue(areaItem, req.body);
      areaItem.parentId = req.body.parentId ?? null; // 不传时默认为null

      // 3、执行更新
      const result = await areaItem.save();
      return result ? res.cc(null, "更新成功") : res.err("更新失败");
    } catch (error) {
      next(error);
    }
  },

  // 给区域绑定图片路径
  async bindPicUrl(req, res, next) {
    try {
      const { id } = req.body;

      // 判断id的数据是否存在
      const areaItem = await Area.findByPk(id);
      if (!areaItem) {
        throw new Error("区域不存在");
      }
      // 修改图片路径
      const result = await areaItem.update({
        picUrl: req.body.picUrl,
      });
      return result ? res.cc(null, "绑定成功") : res.err("绑定失败");
    } catch (error) {
      next(error);
    }
  },

  // 获取当前区域中的所有的绑定信息
  async getAllBindData(req, res, next) {
    try {
      const { areaId } = req.body;
      if (!areaId) throw new Error("请输入区域id");
      // 1、先判断区域表中是否存在
      const areaInfo = await Area.findOne({
        where: {
          id: areaId,
        },
        attributes: {
          exclude: ["createTime", "updateTime", "sort", "parentId"],
        },
      });
      if (!areaInfo) throw new Error("区域不存在");

      // 2、再判断区域/图标绑定表中是否存在
      const areaIconList = await AreaIcon.findAll({
        where: {
          areaId,
        },
        attributes: ["info"],
        include: {
          model: Icon,
          as: "iconInfo",
          attributes: ["iconUrl"],
        },
      });
      if (!areaIconList) throw new Error("获取区域图标失败");

      // 3、组装并返回数据
      const iconList = areaIconList.map((item) => {
        const iconItem = item.get({ plain: true });
        return {
          iconUrl: iconItem.iconInfo.iconUrl,
          info: iconItem.info,
        };
      });

      return res.cc({
        ...areaInfo.get({ plain: true }),
        iconList,
      });
    } catch (error) {
      next(error);
    }
  },

  // 给区域绑定图标
  async bindIcon(req, res, next) {
    try {
      // 1、校验区域是否存在
      const areaItem = await Area.findOne({
        where: {
          id: req.body.areaId,
        },
      });
      if (!areaItem) return res.err("区域不存在");

      // 2、校验图标是否存在
      const iconItem = await Icon.findOne({
        where: {
          id: req.body.iconId,
        },
      });
      if (!iconItem) return res.err("图标不存在");

      // 3、绑定
      const result = await AreaIcon.create({
        areaId: req.body.areaId,
        iconId: req.body.iconId,
        info: req.body.info,
      });
      return result ? res.cc(null, "绑定成功") : res.err("绑定失败");
    } catch (error) {
      next(error);
    }
  },

  // 获取区域树
  async getAreaTree(req, res, next) {
    try {
      const areaTree = await Area.findAll({
        order: [["sort", "ASC"]],
      });
      if (!areaTree) return res.err("获取区域树失败");

      const dataValuesArr = areaTree.map((item) => item.get({ plain: true }));

      // 根据parentId,id组成树结构,下级用children字段接收
      const treeData = await utils.arrToTree(dataValuesArr);
      // 返回树结构
      return res.cc(treeData, "获取区域树成功");
    } catch (error) {
      next(error);
    }
  },

  // 删除区域(必须先删除下级区域)
  async deleteWithoutSub(req, res, next) {
    try {
      const { id } = req.body;
      if (!id) return res.err("请输入区域id");

      const areaItem = await Area.findByPk(id);
      if (!areaItem) {
        return res.err("区域不存在");
      }

      const treeIdList = await this.getTreeIdList(req, res, next);
      if (treeIdList.length > 1) {
        return res.err("请先删除子区域");
      }

      const result = await areaItem.destroy();
      return result ? res.cc(null, "删除成功") : res.err("删除失败");
    } catch (error) {
      next(error);
    }
  },

  // 删除区域(会直接删除所有下级区域)
  async deleteAllSub(req, res, next) {
    try {
      const { id } = req.body;
      if (!id) return res.err("请输入区域id");
      const areaItem = await Area.findByPk(id);
      if (!areaItem) {
        return res.err("区域不存在");
      }
      const treeIdList = await this.getTreeIdList(req, res, next);
      // 删除所有子区域
      const result = await Area.destroy({
        where: {
          id: {
            [Op.in]: treeIdList,
          },
        },
      });
      return result ? res.cc(null, "删除成功") : res.err("删除失败");
    } catch (error) {
      next(error);
    }
  },

  //  校验区域是否存在、编辑时更改信息
  async checkAreaInfo(req, res, next) {
    try {
      const { id, parentId } = req.body;

      // 判断id的数据是否存在
      const areaItem = await Area.findByPk(id);
      if (!areaItem) {
        throw new Error("区域不存在");
      }

      // parentId需存在到数据库中
      if (parentId) {
        const parentArea = await Area.findByPk(parentId);
        if (!parentArea) {
          throw new Error("上级区域不存在");
        }
      }
      const treeIdList = await this.getTreeIdList(req, res, next);
      if (treeIdList.includes(parentId)) {
        throw new Error("区域不能作为自己或下级的子区域");
      }
      return areaItem;
    } catch (error) {
      next(error);
    }
  },

  // 获取区域列表
  async getTreeIdList(req, res, next) {
    try {
      const { id } = req.body;

      if (!id) return res.err("请输入区域id");

      const allIdList = await Area.findAll({
        attributes: ["id", "parentId"],
      });
      if (allIdList.length === 0) return res.cc(null, "区域不存在");
      // 查询树所有子区域id
      return await utils.getAllTreeIdByRecursion(allIdList, req.body.id, [id]);
    } catch (error) {
      next(error);
    }
  },
};
